package org.ala.tcp_feign_client.spring.feign;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Future;

import org.ala.tcp_feign_client.ITcpFeignCallback;
import org.ala.tcp_feign_common.logs.LoggerFactory;
import org.ala.tcp_feign_common.utils.MethodUrlUtils;
import org.slf4j.Logger;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;


/**
 * feign代理
 * 
 * @author 骆毅(ala)
 * @date 2024年4月22日
 */
public class TcpFeignInvocationHandler implements InvocationHandler {
	
	
	static Logger log = LoggerFactory.getLogger("tcp-feign", "client");
	
	
	/**
	 * method的调用方式
	 * <p>	SYNC	同步：方法直接返回结果
	 * <p>	SASYNC	半异步：方法的返回结果是Future类型
	 * <p>	ASYNC	异步：方法的最后一个参数是ITcpFeignCallback类型，并且方法的返回值是void
	 */
	public static final int SYNC = 1,
							SASYNC = 2,
							ASYNC = 3;
	
	
	/**
	 * 真实通讯的feign
	 */
	protected TcpFeign tcpFeign;
	/**
	 * 被代理的接口类型
	 */
	protected Class<?> type;
	/**
	 * 根url
	 */
	protected String path;
	/**
	 * url和Method映射
	 */
	protected Map<Method, MethodAttr> urlMethodMapper;
	

	public TcpFeignInvocationHandler(TcpFeign tcpFeign, Class<?> type, String path) {
		super();
		this.tcpFeign = tcpFeign;
		this.type = type;
		this.path = path;
		
		this.urlMethodMapper = urlMethodMapper();
	}
	/**
	 * 解析method和url映射
	 */
	protected Map<Method, MethodAttr> urlMethodMapper() {
		//	格式化url
		String rootUrl = "";
		if (StringUtils.isEmpty(path)) {rootUrl = "";}
		else {
			if (!path.startsWith("/")) {rootUrl = "/" + path;}
			if (path.endsWith("/")) {rootUrl = path.substring(0, path.length()-1);}
		}
		//	返回值
		Map<Method, MethodAttr> map = new HashMap<>();
		
		//	解析所有的method
		for (Method method : type.getMethods()) {
			String url = methodUrl(rootUrl, method);
			if (StringUtils.isEmpty(url)) {continue;}
			
			int asyncState = methodState(method);
			
			//	方法的异步类型
			map.put(method, new MethodAttr(url, asyncState));
			log.info("[TcpFeignInvocationHandler urlMethodMapper] 解析url与method映射 url:" + url + " method:" + type.getName() + "." + method.getName());
		}
		
		return map;
	}
	/**
	 * 解析方法绑定的url
	 */
	protected String methodUrl(String rootUrl, Method method) {
		//	方法绑定的url
		String url = MethodUrlUtils.findUrl(method);
		if (StringUtils.isEmpty(url)) {return null;}
		if (!url.startsWith("/")) {url = "/" + url;}
		if (!url.endsWith("/")) {url = url + "/";}
		url = rootUrl + url;
		return url;
	}
	/**
	 * 解析方法的同步异步类型
	 */
	protected int methodState(Method method) {
		//	如果方法的返回值类型不为空
		Class<?> returnClass = method.getReturnType();
		
		//	诡异：返回值为void的方法，returnClass.equals(Void.class)既然是false
		if (returnClass != null && !returnClass.getName().equals("void")) {
			//	如果返回值是Future类型，判定为半异步
			if (returnClass.equals(Future.class)) {return SASYNC;}
			//	否则判定为同步
			else {return SYNC;}
		} else {
			//	如果返回值是null，并且最后一个参数是ITcpFeignCallback类型，判定为异步
			if (method.getParameterCount() > 0 
					&& method.getParameterTypes()[method.getParameterCount()-1].equals(ITcpFeignCallback.class)) {
				return ASYNC;
			}
			
		}
		//	默认按同步类型走
		return SYNC;
	}


	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		//	如果没有被url映射过，则直接执行
		if (!ClassUtils.isUserLevelMethod(method)) {
			return method.invoke(proxy, args);
		}
		//	如果被映射过，执行远程通讯
		else {
			MethodAttr attr = urlMethodMapper.get(method);
			if (StringUtils.isEmpty(attr.url)) {throw new Exception("代理方法必须指定url method:" + method.getName() + " proxy:" + proxy.getClass().getName());}
			return tcpFeign.invoke(attr.url, attr.type, args, method.getReturnType());
		}
	}
	
	
	/**
	 * 方法追加参数映射
	 */
	static class MethodAttr {
		//	方法的url
		String url;
		//	方法的同步/异步类型，对应SYNC | SASYNC | ASYNC
		int type;
		public MethodAttr(String url, int type) {
			super();
			this.url = url;
			this.type = type;
		}
		public String getUrl() {
			return url;
		}
		public void setUrl(String url) {
			this.url = url;
		}
		public int getType() {
			return type;
		}
		public void setType(int type) {
			this.type = type;
		}
	}

}
